package com.xp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xp.entity.User;
import com.xp.mapper.UserMapper;
import com.xp.service.AuthService;
import com.xp.utils.JwtUtil;
import com.xp.utils.Result;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 * 
 * @author xp
 * @since 2024-01-01
 */
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private static final Logger log = LoggerFactory.getLogger(AuthServiceImpl.class);

    private final UserMapper userMapper;
    private final JwtUtil jwtUtil;
    private final PasswordEncoder passwordEncoder;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * Token黑名单前缀
     */
    private static final String TOKEN_BLACKLIST_PREFIX = "auth:blacklist:";
    
    /**
     * 登录失败计数前缀
     */
    private static final String LOGIN_FAIL_PREFIX = "auth:fail:";
    
    /**
     * 最大登录失败次数
     */
    private static final int MAX_LOGIN_FAIL_COUNT = 5;
    
    /**
     * 账户锁定时间（分钟）
     */
    private static final int LOCK_TIME_MINUTES = 30;

    /**
     * 用户登录
     */
    @Override
    public Result<Object> login(String username, String password, String clientIp) {
        try {
            // 检查登录失败次数
            String failKey = LOGIN_FAIL_PREFIX + username;
            Integer failCount = (Integer) redisTemplate.opsForValue().get(failKey);
            if (failCount != null && failCount >= MAX_LOGIN_FAIL_COUNT) {
                return Result.error(429, "登录失败次数过多，请30分钟后重试");
            }

            // 查询用户
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getUsername, username);
            User user = userMapper.selectOne(queryWrapper);

            if (user == null) {
                incrementLoginFailCount(username);
                return Result.error(401, "用户名或密码错误");
            }

            // 检查用户状态
            if (user.getStatus() == 0) {
                return Result.error(401, "账户已被禁用");
            }

            // 检查账户是否被锁定
            if (user.getLockTime() != null && user.getLockTime().isAfter(LocalDateTime.now())) {
                return Result.error(401, "账户已被锁定，请稍后重试");
            }

            // 验证密码
            if (!passwordEncoder.matches(password, user.getPassword())) {
                incrementLoginFailCount(username);
                return Result.error(401, "用户名或密码错误");
            }

            // 登录成功，清除失败计数
            redisTemplate.delete(failKey);

            // 生成Token
            String token = jwtUtil.generateToken(user.getId(), user.getUsername());

            // 更新最后登录信息
            updateLastLoginInfo(user.getId(), clientIp);

            // 构建返回数据
            Map<String, Object> data = new HashMap<>();
            data.put("token", token);
            data.put("tokenType", "Bearer");
            data.put("expiresIn", 86400);
            
            Map<String, Object> userInfo = new HashMap<>();
            userInfo.put("id", user.getId());
            userInfo.put("username", user.getUsername());
            userInfo.put("email", user.getEmail());
            data.put("user", userInfo);

            log.info("用户登录成功: {}, IP: {}", username, clientIp);
            return Result.success("登录成功", data);

        } catch (Exception e) {
            log.error("登录异常: {}", e.getMessage(), e);
            return Result.error(500, "登录失败，请稍后重试");
        }
    }

    /**
     * 用户登出
     */
    @Override
    public Result<Object> logout(String token) {
        try {
            if (token != null && jwtUtil.validateToken(token)) {
                // 将Token加入黑名单
                String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
                redisTemplate.opsForValue().set(blacklistKey, "1", 24, TimeUnit.HOURS);
                
                String username = jwtUtil.getUsernameFromToken(token);
                log.info("用户登出成功: {}", username);
            }
            return Result.success("登出成功");
        } catch (Exception e) {
            log.error("登出异常: {}", e.getMessage(), e);
            return Result.error(500, "登出失败");
        }
    }

    /**
     * 获取当前用户信息
     */
    @Override
    public Result<User> getCurrentUser(String token) {
        try {
            if (!validateToken(token)) {
                return Result.error(401, "Token无效或已过期");
            }

            Long userId = jwtUtil.getUserIdFromToken(token);
            User user = userMapper.selectById(userId);
            
            if (user == null) {
                return Result.error(401, "用户不存在");
            }

            // 清除敏感信息
            user.setPassword(null);
            
            return Result.success("获取成功", user);
        } catch (Exception e) {
            log.error("获取用户信息异常: {}", e.getMessage(), e);
            return Result.error(500, "获取用户信息失败");
        }
    }

    /**
     * 验证Token
     */
    @Override
    public boolean validateToken(String token) {
        try {
            if (token == null || token.trim().isEmpty()) {
                return false;
            }

            // 检查Token是否在黑名单中
            if (isTokenBlacklisted(token)) {
                return false;
            }

            // 验证Token格式和签名
            return jwtUtil.validateToken(token);
        } catch (Exception e) {
            log.error("Token验证异常: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 刷新Token
     */
    @Override
    public Result<Object> refreshToken(String token) {
        try {
            if (!validateToken(token)) {
                return Result.error(401, "Token无效或已过期");
            }

            String username = jwtUtil.getUsernameFromToken(token);
            Long userId = jwtUtil.getUserIdFromToken(token);

            // 生成新Token
            String newToken = jwtUtil.generateToken(userId, username);

            // 将旧Token加入黑名单
            String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
            redisTemplate.opsForValue().set(blacklistKey, "1", 24, TimeUnit.HOURS);

            Map<String, Object> data = new HashMap<>();
            data.put("token", newToken);
            data.put("tokenType", "Bearer");
            data.put("expiresIn", 86400);

            return Result.success("Token刷新成功", data);
        } catch (Exception e) {
            log.error("Token刷新异常: {}", e.getMessage(), e);
            return Result.error(500, "Token刷新失败");
        }
    }

    /**
     * 检查Token是否在黑名单中
     * 
     * @param token JWT Token
     * @return 是否在黑名单中
     */
    public boolean isTokenBlacklisted(String token) {
        String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
        return Boolean.TRUE.equals(redisTemplate.hasKey(blacklistKey));
    }

    /**
     * 增加登录失败次数
     * 
     * @param username 用户名
     */
    private void incrementLoginFailCount(String username) {
        String failKey = LOGIN_FAIL_PREFIX + username;
        Integer failCount = (Integer) redisTemplate.opsForValue().get(failKey);
        failCount = failCount == null ? 1 : failCount + 1;
        
        redisTemplate.opsForValue().set(failKey, failCount, LOCK_TIME_MINUTES, TimeUnit.MINUTES);
        
        log.warn("用户 {} 登录失败，当前失败次数: {}", username, failCount);
    }

    /**
     * 更新最后登录信息
     * 
     * @param userId 用户ID
     * @param clientIp 客户端IP
     */
    private void updateLastLoginInfo(Long userId, String clientIp) {
        try {
            String loginTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            userMapper.updateLastLoginInfo(userId, loginTime, clientIp);
        } catch (Exception e) {
            log.error("更新登录信息失败: {}", e.getMessage());
        }
    }
}